
package com.summer.framework.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.summer.framework.api.feign.service.admin.TestApiService;
import com.summer.framework.api.feign.service.admin.UserApiService;
import com.summer.framework.api.feign.service.bill.BillApiService;
import com.summer.framework.common.constant.DistributedLockConstant;
import com.summer.framework.common.constant.RedisConstant;
import com.summer.framework.common.distributed.lock.DistributedLockCallback;
import com.summer.framework.common.entity.Users;
import com.summer.framework.common.entity.response.BooleanResponse;
import com.summer.framework.common.exception.NotFoundException;
import com.summer.framework.common.exception.ParemeterException;
import com.summer.framework.common.service.BaseServiceImpl;
import com.summer.framework.admin.mapper.UserMapper;
import com.summer.framework.admin.service.UserService;
import com.summer.framework.common.exception.admin.UserNameExistException;
import com.summer.framework.common.exception.admin.UserNameNotFoundException;
import com.summer.framework.common.util.StringUtils;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * @auther: 杨杰
 * @date: 2018/12/14 17:52
 * @description:
 */
@Service("userDetailsService")
@Slf4j
@Configuration
public class UserServiceImpl extends BaseServiceImpl<UserMapper, Users> implements UserService {

    @Autowired
    private UserApiService userApiService;

    @Autowired
    private BillApiService billApiService;

    @Override
    //@Cacheable(value = RedisConstant.USER_NAME, key = "#userName")
    public Users getUserByName(String userName) throws UserNameNotFoundException {
        return distributedReadWriteLock.read(new DistributedLockCallback<Users>() {
            @Override
            public String getLockName() {
                return DistributedLockConstant.USER_NAME_LOCK + DistributedLockConstant.COLON + userName;
            }

            @Override
            public Users process() {
                Users user;
                if ((user = getOne(new QueryWrapper<Users>().eq("username", userName))) == null) {
                    throw new UserNameNotFoundException(userName);
                }
                return user;
            }
        });
    }

    @Override
    @Cacheable(value = RedisConstant.USER_ID, key = "#id")
    public Users getById(Serializable id) {
        return Optional.ofNullable(baseMapper
                .selectById(id))
                .orElseThrow(() -> new NotFoundException("无法通过id找到信息"));
    }

    @Override
    public List<Users> list(Wrapper<Users> queryWrapper) {
        List<Users> users = super.baseMapper.selectList(queryWrapper);
        return users;
    }



    @Resource
    private TestApiService testApiService;


    @GlobalTransactional
    @Override
    public void test(Users entity) {
        save(entity);
        billApiService.save(entity.getUsername());
    }


    @Override
    public boolean userNameIsExist(String userName) {
        return distributedReadWriteLock.read(new DistributedLockCallback<Boolean>() {
            @Override
            public String getLockName() {
                return DistributedLockConstant.USER_NAME_LOCK + DistributedLockConstant.COLON + userName;
            }

            @Override
            public Boolean process() {
                return isExist("username", userName);
            }
        });
    }

    @Override
    @GlobalTransactional(timeoutMills = 300000)
    //@Cacheable(value = RedisConstant.USER_ID, key = "#id")
    public boolean save(Users entity) throws ParemeterException, UserNameExistException {
        if (StringUtils.hasEmpty(entity.getUsername(), entity.getPassword())) {
            throw new ParemeterException("用户名或者密码为空");
        }
        return distributedReadWriteLock.write(new DistributedLockCallback<Boolean>() {
            @Override
            public String getLockName() {
                return DistributedLockConstant.USER_NAME_LOCK + DistributedLockConstant.COLON + entity.getUsername();
            }

            @Override
            public Boolean process() {
                if (userNameIsExist(entity.getUsername())) {
                    throw new UserNameExistException(entity.getUsername());
                }
                entity.setPassword(new BCryptPasswordEncoder().encode(entity.getPassword()));
                entity.setCreatedTime(new Date());
                return UserServiceImpl.super.save(entity);
            }
        });
    }


    @Override
    @CachePut(value = RedisConstant.USER_ID, key = "#ids")
    public boolean updateById(Users entity) {
        Users user = getById(entity.getId());
        return distributedReadWriteLock.write(new DistributedLockCallback<Boolean>() {
            @Override
            public String getLockName() {
                return DistributedLockConstant.USER_NAME_LOCK + DistributedLockConstant.COLON + entity.getUsername();
            }

            @Override
            public Boolean process() {
                if ((!entity.getUsername().equals(user.getUsername())) && userNameIsExist(entity.getUsername())) {
                    throw new UserNameExistException(entity.getUsername());
                }
                entity.setPassword(new BCryptPasswordEncoder().encode(entity.getPassword()));
                return UserServiceImpl.super.updateById(entity);
            }
        });
    }

    @Override
    @CacheEvict(value = RedisConstant.USER_ID, key = "#ids")
    public boolean deleteByIds(String... ids) {
        return super.deleteByIds(ids);
    }


}

